package us.versus.them.isod;

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.display.TriangleCulling;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.Event;

import flash.geom.Matrix3D;
import flash.geom.Orientation3D;
import flash.geom.Utils3D;
import flash.geom.Vector3D;

import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Graphics;

import flash.Vector;

/**
 *
 * http://www.senocular.com/flash/tutorials/flash10drawingapi/
 * http://www.nicollet.net/2008/12/flash-10-3d/
 * http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=72&catid=675&threadid=1387302&enterthread=y#5060544
 * http://www.flashandmath.com/flashcs4/cs4planets/index.html
 *
 *
 * http://pixelero.wordpress.com/2008/08/31/flash-10-part-4-of-testing-drawtriangles-old-school-tunnels/
 * http://manfred.dschini.org/2008/05/28/flash-player-10-playing-with-drawtriangles/
 *
 *
 */ 
class Dungeon {
	var tx : Int;
	var tz : Int;

	var x_angle : Float;
	var y_angle : Float;
	var z_angle : Float;

	var map_w:Int;
	var map_h:Int;
	var map_resolution:Int;
	var map:Array< Array< String > >;

	var clip:MovieClip;

	var full:Bitmap;
	var empty:Bitmap;

	public static function main() {
		( new Dungeon( flash.Lib.current ) );
	}

	public function new( ?clip:MovieClip ) {
		if ( null == clip ) {
			clip = flash.Lib.current;
		}
		this.clip = clip;

		full = new Bitmap( new BitmapData( 1, 1 ) );
		full.bitmapData.setPixel( 0, 0, 0 );

		empty = new Bitmap( new BitmapData( 1, 1 ) );
		empty.bitmapData.setPixel( 0, 0, 0x00FF00 );

		map_w = 128;
		map_h = 128;
		map_resolution = 32;

		// TODO: quit c/p'ing this!
		map = new Array< Array< String > >();
		for ( y in 0 ... map_h ) {
			var row = new Array< String >();
			map.push( row );
			for ( x in 0 ... map_w ) {
				row.push( '#' );
			}
		}

		( new SplitDigger() ).dig( map );

		tx = 0;
		tz = 0;

		x_angle = 0;
		y_angle = 0;
		z_angle = 0;

		this.addEventListeners();
	}

	public function addEventListeners() {
		clip.addEventListener( Event.ENTER_FRAME , frame );
		clip.stage.addEventListener( MouseEvent.CLICK, click );
		clip.stage.addEventListener( KeyboardEvent.KEY_DOWN, key );
	}

	public function click( event : MouseEvent ) {
	}

	public function key( event : KeyboardEvent ) {
		switch( event.charCode ) {
			case 88: x_angle--;
			case 89: y_angle--;
			case 90: z_angle--;
			case 114: x_angle = y_angle = z_angle = tz = tx = 0;
			case 120: x_angle++;
			case 121: y_angle++;
			case 122: z_angle++;
			default: {
				switch( event.keyCode ) {
					case 37: tx--; // left
					case 38: tz--; // up
					case 39: tx++; // right
					case 40: tz++; // down
					default: trace( event.charCode + ' / ' + event.keyCode );
				}
			}
		}
	}

	public function frame( event : Dynamic ) {
		var n = Math.floor( flash.Lib.current.stage.stageHeight / map_resolution * 2);

		var m3d = new Matrix3D();
		
		m3d.identity();

		var w = clip.stage.stageWidth;
		var h = clip.stage.stageHeight;

		m3d.appendRotation( x_angle, new Vector3D( 1,0,0 ) );
		m3d.appendRotation( y_angle, new Vector3D( 0,1,0 ) );
		m3d.appendRotation( z_angle, new Vector3D( 0,0,1 ) );
/*
		m3d.pointAt(
			new Vector3D( w / 2 
		);
		pointAt( ?pos : Vector3D, ?at : Vector3D, ?up : Vector3D ) : Void;
		m3d.prependTranslation( tx * map_resolution, 0, tz * map_resolution );
*/

		//trace( x_angle + ',' + y_angle + ',' + z_angle );

		clip.graphics.clear();

		for ( zo in -n ... n ) {
			var z = tz + zo;
			if ( z < 0 )      z += map_h; else
			if ( z >= map_h ) z -= map_h;

			var xo = n + 1;
			while( xo > -n ) {
				xo--;
				var x = tx + xo;
				if ( x < 0 )     x += map_w; else
				if ( x > map_w ) x -= map_w; 

				if ( x >= 0 && x < map_w ) {
					this.box( 
						0 == zo && 0 == xo,
						clip.graphics
						, m3d
						, zo * map_resolution
						, xo * map_resolution
						, '#' == map[ z ][ x ]
					);
				}
			}
		}
	}

	public function box( crap:Bool, g:Graphics, m3d:Matrix3D, z:Int, x:Int, wall:Bool ) {
		// beginBitmapFill(bitmapData);

		var m = map_resolution;
		var y = 0;
      
		//
		//     4 .--------. 5           
		//       |\       |\          floor   :  0,1,2 0,2,3
		//       | \      | \         ceiling :  4,5,6 4,6,7
		//       |7 .--------. 6      back    :  0,1,5 0,5,4
		//       |  |     |  |        front   :  3,7,6 3,6,2
		//     0 .--|-----. 1|        left    :  0,4,7 0,7,3
		//        \ |      \ |        right   :  1,5,6 1,6,2
		//         \|       \|
		//        3 .--------. 2

		var vertices = new Vector< Float >();
		pushy( vertices, [
			  x + 0 , y + 0, z + 0    // 0
			, x + m , y + 0, z + 0    // 1
			, x + m , y + 0, z + m    // 2
			, x + 0 , y + 0, z + m    // 3
			, x + 0 , y - m * 1.5,  z + 0    // 4
			, x + m , y - m * 1.5,  z + 0    // 5
			, x + m , y - m * 1.5,  z + m    // 6
			, x + 0 , y - m * 1.5,  z + m    // 7
		] );
		var transformed = new Vector< Float >();

		m3d.transformVectors( vertices, transformed );
		var idx = 0;

		var mangled = new Vector< Float >();
		while ( idx < vertices.length ) {
			//projected[2*i]   = ((transformed[3*i] / z) + 1) * width;
			//projected[2*i+1] = ((transformed[3*i+1] / z) + 1) * height;

			//transformed[ idx + 0 ] += transformed[ idx + 2 ];
			transformed[ idx + 1 ] += transformed[ idx + 2 ];

			mangled.push( transformed[ idx + 0 ] );
			mangled.push( transformed[ idx + 1 ] );
			idx += 3;
		}

		if ( wall ) {
			// need to flip around drawing order
			var faces = [
				  [ 0,1,2, 0,2,3 ] // floor   
				, [ 0,1,5, 0,5,4 ] // back    
				, [ 0,4,7, 0,7,3 ] // left    
				, [ 1,5,6, 1,6,2 ] // right   
				, [ 3,7,6, 3,6,2 ] // front   
				, [ 4,5,6, 4,6,7 ] // ceiling 
			];
			var colors = [ 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0xb33333 ];
			for ( i in 0 ... faces.length ) {
				var floor_indices = new Vector< Int >();
				pushii( floor_indices, faces[ i ] );
				g.beginFill( colors[ i ] );
				g.drawTriangles( mangled, floor_indices );
				g.endFill();
			}
		} else {
			var floor_indices = new Vector< Int >();
			pushii( floor_indices, [ 0,1,2, 0,2,3 ] );
			g.beginFill( 0xAAAAAA );
			g.drawTriangles( mangled, floor_indices, null, TriangleCulling.NEGATIVE );
			g.endFill();
		}
	}

	public function pushy( v:Vector< Float >, a:Array< Float > ) {
		for ( f in a ) {
			v.push( f );
		}
	}

	public function pushi( v:Vector< Float >, a:Array< Int > ) {
		for ( f in a ) {
			v.push( f );
		}
	}

	public function pushii( v:Vector< Int >, a:Array< Int > ) {
		for ( f in a ) {
			v.push( f );
		}
	}


	public function v( ?x:Float, ?y:Float, ?z:Float ) {
		var v = new Vector< Float >( 3 );
		v[ 0 ] = if ( null == x ) 0; else x;
		v[ 1 ] = if ( null == y ) 0; else y;
		v[ 2 ] = if ( null == z ) 0; else z;
		return v;
	}

}
